package edu.nf.dbutil;

import edu.nf.dbutil.utils.Column;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * 行处理器工具类，将一行记录转换为Array、Map、Bean等
 * @author yang
 * @date 2024-05-08
 */
public class RowProcessor {
    /**
     * 将结果集转换为Array
     * @param rs
     * @return
     * @throws SQLException
     * 首先初始化数组对象（长度为 结果集对象 的 元数据信息列数）
     * 将结果集对象（一行）数据循环添加至数组对象中
     * 返回数组对象
     */
    public static Object[] toArray(ResultSet rs) throws SQLException {
        //通过rs的元数据获取查询的列数并构建Object[]
        Object[] result = new Object[rs.getMetaData().getColumnCount()];
        for (int i = 0; i < result.length; i++) {
            result[i] = rs.getObject(i + 1);
        }
        return result;
    }

    /**
     * 根据列名获取某个值
     * @param rs 结果集
     * @param columnName 列名
     * @return 当前列的值
     * @throws SQLException sql异常
     * rs.getObject(String)：根据 字符串 查询 结果集对象 对应的参数值
     */
    public static Object toValue(ResultSet rs, String columnName)throws SQLException{
        return rs.getObject(columnName);
    }

    /**
     * 根据下标获取某个值
     * @param rs 结果集
     * @param columnIndex 下标
     * @return 当前列的值
     * @throws SQLException sql异常
     *  return rs.getObject(int)：根据 下标（从 1 开始） 查询结果集对象 对应的参数值;
     */
    public static Object toValue(ResultSet rs, Integer columnIndex)throws SQLException{
        return rs.getObject(columnIndex);
    }

    /**
     * 将一条记录转换为 map 对象
     * @param rs
     * @return
     * 初始化 Map 对象
     * 通过 rs.getMetaData() 获取 结果集对象 的 元数据信息
     * 通过遍历元数据信息，对 Map 对象进行赋值
     * map.put(metaData.getColumnLabel(i), rs.getObject(i)) ->
     * map.put(id, 1)、
     * map.put(name, "zhangsan")、
     * map.put(password, "123456")...
     * 最后返回 Map 对象
     */
    public static Map<String, Object> toMap(ResultSet rs)throws SQLException{
        Map<String, Object> map = new HashMap<>();
        //获取元数据得到列名
        ResultSetMetaData metaData = rs.getMetaData();
        for (int i = 1; i <= metaData.getColumnCount(); i++) {
            //metaDate.getColumnLabel()方法获取当前列的列名
            //rs.getObject(下标)获取当前列的值
            map.put(metaData.getColumnLabel(i), rs.getObject(i));
        }
        return map;
    }

    /**
     * (自)
     * 将一条记录转换为实体对象
     * @param rs
     * @param beanClass
     * @return
     * @param <T>
     * @throws SQLException
     */
    public static <T> T toBean(ResultSet rs, Class<T> beanClass) throws SQLException {
        //获取结果集元数据对象
        ResultSetMetaData metaData = rs.getMetaData();
        try {
            //实例化实体对象
            T bean = beanClass.getConstructor().newInstance();
            //通过列名获取实体对象对应的属性名
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                String columnLabel = new RowProcessor().getFieldName(metaData.getColumnLabel(i), beanClass);
                //获取当列数据
                Object value = rs.getObject(i);
                //获取实体类字段名称
                Field field = beanClass.getDeclaredField(columnLabel);
                //设置访问权限
                field.setAccessible(true);
                //将数据封装到实体对象中
                field.set(bean, value);

            }
            return bean;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * (自)
     * 根据数据表字段名称，获取对应实体对象的属性名
     * 如：u_name -> name
     * @param columnLabel 数据表字段名称
     * @param beanCalss 实体对象的 Class 对象
     * @return 实体对象属性名称
     * @param <T> T 类型的实体对象
     */
    private <T> String getFieldName(String columnLabel, Class<T> beanCalss){
        //获取实体对象的所有字段列表
        Field[] fields = beanCalss.getDeclaredFields();

        //循环遍历字段列表
        for (Field field : fields){
            //判断字段列表上是否存在 Column 注解
            if (field.isAnnotationPresent(Column.class)){
                //获取 Column 的值
                String value = field.getAnnotation(Column.class).value();

                //判断 Column 注解的值是否与数据表字段名称一样
                if (value != null && value.equals(columnLabel)){
                    //如果一样，则返回对应字段对象的名称（实体对象的属性名称）
                    return field.getName();
                }
            }
        }
        //如果不一样，则返回数据表字段名称
        return columnLabel;
    }

    /**
     * 将一条记录转换为实体对象
     * @param rs
     * @param beanClass
     * @return
     * @param <T>
     * @throws SQLException
     * 返回 BeanProcessor.createBean(ResultSet rs, Class<T> beanClass) 方法的实体对象
     */
    public static <T> T toBean2(ResultSet rs, Class<T> beanClass) throws SQLException {
        return BeanProcessor.createBean(rs, beanClass);
    }
}
